#ifndef _TLIB_TFUNCTION_H_
#define _TLIB_TFUNCTION_H_

#include "tbind.h"
//http://github.com/ToyAuthor/functional
namespace tlib
{
namespace _functional
{

template<typename R, typename S>
struct core_base
{
  virtual ~core_base()
  {
  }
  virtual core_base* clone() const=0;
  virtual R CallFunction(const S &s) const=0;
  virtual void SetObject(void *p)
  {
  }
};

template<typename R, typename S, typename F>
struct core_function: core_base<R, S>
{
  const F _f;

  explicit core_function(const F &f) :
      _f(f)
  {
  }

  virtual core_base<R, S>* clone() const
  {
    return new core_function(_f);
  }

  R CallFunction(const S &s) const
  {
    return s.Do(type<R>(), _f);
  }
};

template<typename R, typename F, typename C>
struct member_function
{
  explicit member_function(const member_function &other) :
      pFunction(other.pFunction), pObject(other.pObject)
  {
  }
  explicit member_function(const F &f) :
      pFunction(f), pObject(0)
  {
  }
  explicit member_function() :
      pFunction(0), pObject(0)
  {
  }

  const F pFunction;
  C* pObject;

  inline void set_this(void *app)
  {
    pObject = static_cast<C*>(app);
  }

  inline R operator()() const
  {
    return (pObject->*pFunction)();
  }

  template<typename A1>
  inline R operator()(A1 a1) const
  {
    return (pObject->*pFunction)(a1);
  }

  template<typename A1, typename A2>
  inline R operator()(A1 a1, A2 a2) const
  {
    return (pObject->*pFunction)(a1, a2);
  }

  template<typename A1, typename A2, typename A3>
  inline R operator()(A1 a1, A2 a2, A3 a3) const
  {
    return (pObject->*pFunction)(a1, a2, a3);
  }

  template<typename A1, typename A2, typename A3, typename A4>
  inline R operator()(A1 a1, A2 a2, A3 a3, A4 a4) const
  {
    return (pObject->*pFunction)(a1, a2, a3, a4);
  }
};

template<typename R, typename S, typename F, typename C>
struct core_member_function: core_base<R, S>
{
  F _f;

  explicit core_member_function(const F &f) :
      _f(f)
  {
  }

  virtual core_base<R, S>* clone() const
  {
    return new core_member_function(_f);
  }
  R CallFunction(const S &s) const
  {
    return s.Do(type<R>(), _f);
  }
  void SetObject(void *app)
  {
    _f.set_this(app);
  }
};

template<typename T, typename S>
struct core_bind: core_base<typename T::result_type, S>
{
  typedef typename T::result_type result_type;

  const T obj;

  explicit core_bind(const T &b) :
      obj(b)
  {
  }

  virtual core_base<result_type, S>* clone() const
  {
    return new core_bind(obj);
  }

  virtual result_type CallFunction(const S &s) const
  {
    return obj.eval(s);
  }
};

} //namespace _functional

template<typename R, typename S>
struct function_base
{
  function_base() :
      pCore(0)
  {
  }
  ~function_base()
  {
    delete pCore;
  }

  _functional::core_base<R, S> *pCore;

  template<typename C>
  inline void set(C* c)
  {
    pCore->SetObject((void*) (c));
  }

  function_base(const function_base &other)
  {
    if (other.pCore)
    {
      pCore = other.pCore->clone();
    }
  }

  function_base& operator=(const function_base &other)
  {
    delete pCore;
    pCore = other.pCore ? other.pCore->clone() : other.pCore;
    return *this;
  }
};

template<typename S>
struct function
{
};

template<typename R>
struct function<R()> : function_base<R, storage0>
{
  typedef R
  (*Fn)();
  typedef storage0 St;
  using function_base<R, St>::pCore;

  function()
  {
  }
  function(Fn f)
  {
    pCore = new _functional::core_function<R, St, Fn>(f);
  }
  function operator=(Fn f)
  {
    delete pCore;
    pCore = new _functional::core_function<R, St, Fn>(f);
    return *this;
  }

  template<typename C>
  function(R (C::*f)())
  {
    typedef _functional::member_function<R, R (C::*)(), C> F;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
  }
  template<typename C>
  function(R (C::*f)(), C* c)
  {
    typedef _functional::member_function<R, R (C::*)(), C> F;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
    pCore->SetObject((void*) (c));
  }
  template<typename C>
  function operator=(R (C::*f)())
  {
    typedef _functional::member_function<R, R (C::*)(), C> F;
    delete pCore;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
    return *this;
  }

  template<typename A, typename B, typename C>
  function(const bind_t<A, B, C> &b)
  {
    pCore = new _functional::core_bind<bind_t<A, B, C>, St>(b);
  }
  template<typename A, typename B, typename C>
  function operator=(const bind_t<A, B, C> &b)
  {
    delete pCore;
    pCore = new _functional::core_bind<bind_t<A, B, C>, St>(b);
    return *this;
  }

  R operator()() const
  {
    return pCore->CallFunction(St());
  }
};

template<typename R, typename P1>
struct function<R(P1)> : function_base<R, storage1<P1> >
{
  typedef R (*Fn)(P1);
  typedef storage1<P1> St;
  using function_base<R, St>::pCore;

  function()
  {
  }

  function(Fn f)
  {
    pCore = new _functional::core_function<R, St, Fn>(f);
  }
  function operator=(Fn f)
  {
    delete pCore;
    pCore = new _functional::core_function<R, St, Fn>(f);
    return *this;
  }

  template<typename C>
  function(R (C::*f)(P1))
  {
    typedef _functional::member_function<R, R (C::*)(P1), C> F;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
  }
  template<typename C>
  function(R (C::*f)(P1), C* c)
  {
    typedef _functional::member_function<R, R (C::*)(P1), C> F;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
    pCore->SetObject((void*) (c));
  }
  template<typename C>
  function operator=(R (C::*f)(P1))
  {
    typedef _functional::member_function<R, R (C::*)(P1), C> F;
    delete pCore;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
    return *this;
  }

  template<typename A, typename B, typename C>
  function(const bind_t<A, B, C> &b)
  {
    pCore = new _functional::core_bind<bind_t<A, B, C>, St>(b);
  }
  template<typename A, typename B, typename C>
  function operator=(const bind_t<A, B, C> &b)
  {
    delete pCore;
    pCore = new _functional::core_bind<bind_t<A, B, C>, St>(b);
    return *this;
  }

  R operator()(P1 p1) const
  {
    return pCore->CallFunction(St(p1));
  }
};

template<typename R, typename P1, typename P2>
struct function<R(P1, P2)> : function_base<R, storage2<P1, P2> >
{
  typedef R (*Fn)(P1, P2);
  typedef storage2<P1, P2> St;
  using function_base<R, St>::pCore;

  function()
  {
  }
  function(Fn f)
  {
    pCore = new _functional::core_function<R, St, Fn>(f);
  }
  function operator=(Fn f)
  {
    delete pCore;
    pCore = new _functional::core_function<R, St, Fn>(f);
    return *this;
  }

  template<typename C>
  function(R (C::*f)(P1, P2))
  {
    typedef _functional::member_function<R, R (C::*)(P1, P2), C> F;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
  }
  template<typename C>
  function(R (C::*f)(P1, P2), C* c)
  {
    typedef _functional::member_function<R, R (C::*)(P1, P2), C> F;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
    pCore->SetObject((void*) (c));
  }
  template<typename C>
  function operator=(R (C::*f)(P1, P2))
  {
    typedef _functional::member_function<R, R (C::*)(P1, P2), C> F;
    delete pCore;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
    return *this;
  }

  template<typename A, typename B, typename C>
  function(const bind_t<A, B, C> &b)
  {
    pCore = new _functional::core_bind<bind_t<A, B, C>, St>(b);
  }
  template<typename A, typename B, typename C>
  function operator=(const bind_t<A, B, C> &b)
  {
    delete pCore;
    pCore = new _functional::core_bind<bind_t<A, B, C>, St>(b);
    return *this;
  }

  R operator()(P1 p1, P2 p2) const
  {
    return pCore->CallFunction(St(p1, p2));
  }
};

template<typename R, typename P1, typename P2, typename P3>
struct function<R(P1, P2, P3)> : function_base<R, storage3<P1, P2, P3> >
{
  typedef R (*Fn)(P1, P2, P3);
  typedef storage3<P1, P2, P3> St;
  using function_base<R, St>::pCore;

  function()
  {
  }
  function(Fn f)
  {
    pCore = new _functional::core_function<R, St, Fn>(f);
  }
  function operator=(Fn f)
  {
    delete pCore;
    pCore = new _functional::core_function<R, St, Fn>(f);
    return *this;
  }

  template<typename C>
  function(R (C::*f)(P1, P2, P3))
  {
    typedef _functional::member_function<R, R (C::*)(P1, P2, P3), C> F;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
  }
  template<typename C>
  function(R (C::*f)(P1, P2, P3), C* c)
  {
    typedef _functional::member_function<R, R (C::*)(P1, P2, P3), C> F;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
    pCore->SetObject((void*) (c));
  }
  template<typename C>
  function operator=(R (C::*f)(P1, P2, P3))
  {
    typedef _functional::member_function<R, R (C::*)(P1, P2, P3), C> F;
    delete pCore;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
    return *this;
  }

  template<typename A, typename B, typename C>
  function(const bind_t<A, B, C> &b)
  {
    pCore = new _functional::core_bind<bind_t<A, B, C>, St>(b);
  }
  template<typename A, typename B, typename C>
  function operator=(const bind_t<A, B, C> &b)
  {
    delete pCore;
    pCore = new _functional::core_bind<bind_t<A, B, C>, St>(b);
    return *this;
  }

  R operator()(P1 p1, P2 p2, P3 p3) const
  {
    return pCore->CallFunction(St(p1, p2, p3));
  }
};

template<typename R, typename P1, typename P2, typename P3, typename P4>
struct function<R(P1, P2, P3, P4)> : function_base<R, storage4<P1, P2, P3, P4> >
{
  typedef R (*Fn)(P1, P2, P3, P4);
  typedef storage4<P1, P2, P3, P4> St;
  using function_base<R, St>::pCore;

  function()
  {
  }
  function(Fn f)
  {
    pCore = new _functional::core_function<R, St, Fn>(f);
  }
  function operator=(Fn f)
  {
    delete pCore;
    pCore = new _functional::core_function<R, St, Fn>(f);
    return *this;
  }

  template<typename C>
  function(R (C::*f)(P1, P2, P3, P4))
  {
    typedef _functional::member_function<R, R (C::*)(P1, P2, P3, P4), C> F;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
  }
  template<typename C>
  function(R (C::*f)(P1, P2, P3, P4), C* c)
  {
    typedef _functional::member_function<R, R (C::*)(P1, P2, P3, P4), C> F;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
    pCore->SetObject((void*) (c));
  }
  template<typename C>
  function operator=(R (C::*f)(P1, P2, P3, P4))
  {
    typedef _functional::member_function<R, R (C::*)(P1, P2, P3, P4), C> F;
    delete pCore;
    pCore = new _functional::core_member_function<R, St, F, C>(F(f));
    return *this;
  }

  template<typename A, typename B, typename C>
  function(const bind_t<A, B, C> &b)
  {
    pCore = new _functional::core_bind<bind_t<A, B, C>, St>(b);
  }
  template<typename A, typename B, typename C>
  function operator=(const bind_t<A, B, C> &b)
  {
    delete pCore;
    pCore = new _functional::core_bind<bind_t<A, B, C>, St>(b);
    return *this;
  }

  R operator()(P1 p1, P2 p2, P3 p3, P4 p4) const
  {
    return pCore->CallFunction(St(p1, p2, p3, p4));
  }
};
} //namespace tlib

#endif
